#include "indexthread.h"
#include <QTextStream>
#include <QDir>
#include <QFileInfo>
#include <QDebug>
#include <QFile>

#define DEBUG 0

void IndexThread::run() {
    QString path = this->file_name;
    QFile db_file(path);
    bool is_db_exist = false;
    if (db_file.exists()) {
        is_db_exist = true;
    }
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "index");
    db.setDatabaseName(path);
    db.open();
    if (!is_db_exist) {
        db.exec("CREATE TABLE `index`("
                "`id` INTEGER PRIMARY KEY AUTOINCREMENT,"
                "`word` TEXT,"
                "`line_text` TEXT,"
                "`filename` TEXT,"
                "`line_number` INT(11))");
    }

    this->file_list.clear();
    QDir dir(this->dir);
    QStringList name_filters;
    name_filters << "*.c" << "*.cpp" << "*.h";
    QStringList file_list = dir.entryList(name_filters, QDir::Files | QDir::Readable, QDir::Name);
    QStringList sub_dir_list = dir.entryList(QDir::Dirs);

    for (QString file : file_list) {
        this->file_list.append(this->dir + file);
    }

    for (QString file : sub_dir_list) {
        if (file.left(1) == ".") {
            continue;
        }
        QFileInfo info(this->dir + file);
        if (info.isDir()) {
            this->goto_sub_dir(this->dir + file + QDir::separator());
        }
    }

    QSqlQuery q = db.exec("SELECT `filename` FROM `index` GROUP BY `filename`");
    while (q.next()) {
        this->file_list.removeOne(q.value("filename").toString());
    }

    int current_index = 0, index_count = this->file_list.length();
    for (QString file_path : this->file_list) {
        QStringList file_name_list = file_path.split(QDir::separator());
        while (file_name_list.last() == "") {
            file_name_list.pop_back();
        }
        QString f_name = file_name_list.last();
        emit this->send_file_msg(current_index, index_count, f_name, "正在索引文件");
        ++current_index;
        this->word_list = this->read_file(file_path);
        QList<Searched> code_list = this->lexer();
        QString sql = "INSERT INTO `index`(`word`, `line_text`, `filename`, `line_number`) VALUES ";
        int count = 0;
        for (Searched search : code_list) {
            ++count;
            QString code = search.code
                    .replace("'", "{|}")
                    .replace("\"", "{||}");
            QString line_text = search.line_text
                    .replace("'", "{|}")
                    .replace("\"", "{||}");
            sql += "("
                   "'" + code + "',"
                   "'" + line_text + "',"
                   "'" + file_path + "'," +
                   QVariant(search.line_number).toString() +
                   "),";
        }
        if (count == 0) {
            sql += "('','','" + file_path + "',0)";
        } else {
            sql = sql.left(sql.length() - 1);
        }
        db.exec(sql);
        QSqlError err = db.lastError();
        if (err.isValid()) {
            switch (err.type()) {
            case QSqlError::NoError:
            {
                emit this->send_error("No Error", err.databaseText(), sql);
                return;
            }
            case QSqlError::ConnectionError:
            {
                emit this->send_error("ConnectionError", err.databaseText(), sql);
                return;
            }
            case QSqlError::StatementError:
            {
                emit this->send_error("StatementError", err.databaseText(), sql);
                return;
            }
            case QSqlError::TransactionError:
            {
                emit this->send_error("TransactionError", err.databaseText(), sql);
                return;
            }
            case QSqlError::UnknownError:
            {
                emit this->send_error("UnknownError", err.databaseText(), sql);
                return;
            }
            }
            db.close();
            QSqlDatabase::removeDatabase("index");
            return;
        }
        emit this->send_file_msg(current_index, index_count, f_name, "已索引文件");
    }
    db.close();
    QSqlDatabase::removeDatabase("index");
    emit this->send_finished();
}

void IndexThread::goto_sub_dir(QString path) {
    QString fold_path = path;
    QDir dir(fold_path);
    QStringList name_filters;
    name_filters << "*.c" << "*.cpp" << "*.h";
    QStringList file_list = dir.entryList(name_filters, QDir::Files | QDir::Readable, QDir::Name);
    QStringList sub_dir_list = dir.entryList(QDir::Dirs);

    for (QString file : file_list) {
        this->file_list.append(fold_path + file);
    }

    for (QString file : sub_dir_list) {
        if (file.left(1) == ".") {
            continue;
        }
        QFileInfo info(fold_path + file);
        if (info.isDir()) {
            this->goto_sub_dir(fold_path + file + QDir::separator());
        }
    }
}

QList<Searched> IndexThread::read_file(QString file_name) {
    QList<Searched> list;
    QString text = "";
    QFile file(file_name);

    bool is_open = file.open(QIODevice::ReadOnly | QIODevice::Text);
    if (!is_open) {
        return list;
    }

    QTextStream ts(&file);
    text = ts.readLine();
    int line_number = 0;
    while (!text.isNull()) {
        ++line_number;
        Searched s;
        s.code = text;
        s.file_name = file_name;
        s.line_number = line_number;
        list.append(s);
        text = ts.readLine();
    }
    file.close();

    return list;
}

QList<Searched> IndexThread::lexer() {
    QList<Searched> lex_list;  // 无语法错误时返回该变量
    QString lex_buffer = "";
    bool is_block_comment = false;  // 是否为块注释
    bool is_identifier = false;  // 是否为标识符
    bool is_number = false;  // 是否为数字
    bool is_pretreatment = false;  // 是否为预处理
    bool is_string = false;
    bool is_macro_continue = false;
    bool is_continue = false;
    bool is_break = false;
    bool is_change = false;
//    bool in_small_brackets = false;
    bool in_middle_brackets = false;
    bool in_big_brackets = false;
    char next_c = '\0';
    char str_start = '\0';
    QString comment_buffer = "";
//    QString small_bracket_stack = "";
    QString middle_bracket_stack = "";
    QString big_bracket_stack = "";
    Searched s;

    for (Searched item : this->word_list) {
        if (lex_buffer.length() > 0) {
            s.code = lex_buffer;
            lex_list.append(s);
            lex_buffer.clear();
        }
        QString code = item.code.trimmed();
        if (code.length() <= 0) {
            continue;
        }
        s.file_name = item.file_name;
        s.line_number = item.line_number;
        s.line_text = item.code;
        int len = code.length();
        for (int i = 0; i < len; ++i) {
            char c = code[i].toLatin1();

            // 处理块注释
            if (is_block_comment) {
                // 块注释一直查询到*/结束
                comment_buffer.clear();
                comment_buffer += c;
                ++i;
                if (i >= len) {
                    continue;
                }
                comment_buffer += code[i].toLatin1();
                if (comment_buffer == "*/") {
                    is_block_comment = false;
                    comment_buffer.clear();
                } else {
                    --i;
                }
                continue;
            }

            // 如果当前为字符串模式
            if (is_string) {
                while (true) {
                    if (i >= len) {
                        break;
                    }
                    c = code[i].toLatin1();
                    if (is_change) {
                        is_change = false;
//                        lex_buffer += c;
                        ++i;
                        continue;
                    } else {
                        if (c == '\\') {
                            is_change = true;
//                            lex_buffer += c;
                            ++i;
                            continue;
                        } else if (c == str_start) {
                            if (lex_buffer.length() > 0) {
//                                s.code = lex_buffer;
//                                lex_list.append(s);
                                lex_buffer.clear();
                            }
//                            lex_buffer += c;
//                            s.code = lex_buffer;
//                            lex_list.append(s);
                            lex_buffer.clear();
                            is_string = false;
                            is_continue = true;
                            break;
                        } else {
//                            lex_buffer += c;
                        }
                    }
                    ++i;
                }
            }

            // 如果在上一行遇到了宏继续符
            if (is_macro_continue) {
                c = code[code.length() - 1].toLatin1();
                if (c == '\\') {
                    is_macro_continue = true;
                } else {
                    is_macro_continue = false;
                }
                break;
            }

            // 如果是预处理模式
            if (is_pretreatment) {
                // 跳过中间的空白符
                while (c == ' ' || c == '\t') {
                    ++i;
                    if (i >= len) {
                        is_break = true;
                        break;
                    }
                    c = code[i].toLatin1();
                }
                if (is_break) {
                    is_break = false;
                    if (code[code.length() - 1].toLatin1() == '#') {
                        code.remove(code.length() - 1, 1);
                    }
                    break;
                }
                // 先把下一段空白符前的字符串取出来
                while (c >= 'a' && c <= 'z') {
                    lex_buffer += c;
                    ++i;
                    if (i >= len) {
                        break;
                    }
                    c = code[i].toLatin1();
                }
                // 只识别define，其他全部跳过
                if (lex_buffer != "define") {
                    is_pretreatment = false;
                    lex_buffer.clear();
                    break;
                } else {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                    ++i;
                    if (i >= len) {
                        break;
                    }
                    c = code[i].toLatin1();
                    // 把中间所有的字母、数字、下划线取出来
                    while ((c >= 'a' && c <= 'z') ||
                           (c >= 'A' && c <= 'Z') ||
                           (c >= '0' && c <= '9') ||
                           c == '_') {
                        lex_buffer += c;
                        ++i;
                        if (i >= len) {
                            break;
                        }
                        c = code[i].toLatin1();
                    }
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                    // 看到继续符则返回循环继续
                    if (code[code.length() - 1] == '\\') {
                        is_macro_continue = true;
                    }
                    is_pretreatment = false;
                    is_break = true;
                }
            }
            if (is_continue) {
                is_continue = false;
                continue;
            }
            if (is_break) {
                is_break = false;
                break;
            }

            switch (c) {
            case '(':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
//                in_small_brackets = true;
//                small_bracket_stack += c;
                continue;
            }
                break;
            case '[':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
                in_middle_brackets = true;
                middle_bracket_stack += c;
            }
                break;
            case '{':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
                in_big_brackets = true;
                big_bracket_stack += c;
            }
                break;
            case ')':
            {
//                small_bracket_stack = small_bracket_stack.left(small_bracket_stack.length() - 1);
//                if (small_bracket_stack.length() == 0) {
//                    in_small_brackets = false;
                    continue;
//                }
            }
                break;
            case ']':
            {
                middle_bracket_stack = middle_bracket_stack.left(middle_bracket_stack.length() - 1);
                if (middle_bracket_stack.length() == 0) {
                    in_middle_brackets = false;
                    continue;
                }
            }
                break;
            case '}':
            {
                big_bracket_stack = big_bracket_stack.left(big_bracket_stack.length() - 1);
                if (big_bracket_stack.length() == 0) {
                    in_big_brackets = false;
                    continue;
                }
            }
                break;
            }

            if (in_middle_brackets || in_big_brackets) {
                continue;
            }

            switch (c) {
            case '\t':
            case '\r':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
                is_identifier = false;
                is_number = false;
                // 跳过空白符
            }
                break;
            case '#':
            {
                // 如果lex_buffer中没有数据，则认为是预处理符
                if (lex_buffer.length() <= 0) {
                    is_pretreatment = true;
                }
            }
                continue;
            case ';':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
                is_number = false;
                is_identifier = false;
//                s.code = ";";
//                lex_list.append(s);
            }
                break;
            case '/':
            {
                ++i;
                if (i >= len) {
                    continue;
                }
                c = code[i].toLatin1();
                if (c == '/') {
                    is_break = true;
                    break;
                } else if (c == '*') {
                    is_block_comment = true;
                    continue;
                } else if (c == '=') {
                    s.code = "/=";
//                    lex_list.append(s);
                } else {
                    --i;
                    s.code = "/";
//                    lex_list.append(s);
                }
            }
                break;
            case '+':
            case '|':
            case '&':
            case '=':
            {
                if (is_identifier || is_number) {
                    if (lex_buffer.length() > 0) {
                        s.code = lex_buffer;
                        lex_list.append(s);
                        lex_buffer.clear();
                    }
                }
                lex_buffer = c;
                ++i;
                if (i >= len) {
                    continue;
                }
                char next_c = code[i].toLatin1();
                if (next_c == c || next_c == '=') {
                    lex_buffer += next_c;
                } else {
                    --i;
                }
//                s.code = lex_buffer;
//                lex_list.append(s);
                lex_buffer.clear();
            }
                break;
            case '-':
            {
                if (is_identifier || is_number) {
                    if (lex_buffer.length() > 0) {
                        s.code = lex_buffer;
                        lex_list.append(s);
                        lex_buffer.clear();
                    }
                }
                lex_buffer = c;
                ++i;
                if (i >= len) {
                    continue;
                }
                char next_c = code[i].toLatin1();
                if (next_c == c || next_c == '=' || next_c == '>') {
                    lex_buffer += next_c;
                } else {
                    --i;
                }
//                s.code = lex_buffer;
//                lex_list.append(s);
                lex_buffer.clear();
            }
                break;
            case '*':
            case '%':
            case '^':
            case '!':
            {
                lex_buffer = c;
                ++i;
                if (i >= len) {
                    continue;
                }
                next_c = code[i].toLatin1();
                if (next_c == '=') {
                    lex_buffer += next_c;
                } else {
                    --i;
                }
//                s.code = lex_buffer;
//                lex_list.append(s);
                lex_buffer.clear();
            }
                break;
            case '>':
            case '<':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
                lex_buffer = c;
                ++i;
                if (i >= len) {
                    continue;
                }
                char next_c = code[i].toLatin1();
                if (next_c == c) {
                    lex_buffer += next_c;
                    ++i;
                    if (i >= len) {
                        continue;
                    }
                    char third_c = code[i].toLatin1();
                    if (third_c == '=') {
                        lex_buffer += third_c;
                    }
                } else if (next_c == '=') {
                    lex_buffer += next_c;
                }
//                s.code = lex_buffer;
//                lex_list.append(s);
                lex_buffer.clear();
            }
                break;
            case '~':case '.':case ',':
            case '?':case ':':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
//                lex_buffer += c;
//                s.code = lex_buffer;
//                lex_list.append(s);
//                lex_buffer.clear();
            }
                break;
            case 'q':case 'w':case 'e':case 'r':case 't':case 'y':case 'u':case 'i':case 'o':case 'p':case 'a':
            case 's':case 'd':case 'f':case 'g':case 'h':case 'j':case 'k':case 'l':case 'z':case 'x':case 'c':
            case 'v':case 'b':case 'n':case 'm':
            case 'Q':case 'W':case 'E':case 'R':case 'T':case 'Y':case 'U':case 'I':case 'O':case 'P':case 'A':
            case 'S':case 'D':case 'F':case 'G':case 'H':case 'J':case 'K':case 'L':case 'Z':case 'X':case 'C':
            case 'V':case 'B':case 'N':case 'M':
            case '_':
            {
                if (!is_identifier) {
                    if (lex_buffer.length() > 0) {
                        s.code = lex_buffer;
                        lex_list.append(s);
                        lex_buffer.clear();
                    }
                    is_identifier = true;
                }
                lex_buffer += c;
            }
                break;
            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':
            {
                if (is_identifier) {
                    lex_buffer += c;
                } else if (!is_number) {
                    if (lex_buffer.length() > 0) {
                        s.code = lex_buffer;
                        lex_list.append(s);
                        lex_buffer.clear();
                    }
                    is_number = true;
//                    lex_buffer += c;
                }
            }
                break;
            case ' ':
            {
                if (lex_buffer.length() > 0) {
                    s.code = lex_buffer;
                    lex_list.append(s);
                    lex_buffer.clear();
                }
                is_identifier = false;
                is_number = false;
            }
                break;
            case '\'':
            case '"':
            {
                if (lex_buffer.length() > 0) {
//                    s.code = lex_buffer;
//                    lex_list.append(s);
                    lex_buffer.clear();
                }
                str_start = c;
                is_string = true;
//                lex_buffer += c;
//                s.code = lex_buffer;
//                lex_list.append(s);
                lex_buffer.clear();
            }
                break;
            default:
            {
                lex_buffer += QVariant((int)c).toString();
            }
                break;
            }
            if (is_break) {
                is_break = false;
                break;
            }
        }
    }
    if (lex_buffer.length() > 0) {
        s.code = lex_buffer;
        lex_list.append(s);
    }
    return lex_list;
}
